#include <Windows.h>
#include <sstream>
#include <iostream>

#include <osg/io_utils>

#include <osgGA/TrackballManipulator>
#include <osgGA/StateSetManipulator>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <ft2build.h>
#include <freetype/freetype.h>

#include "Cube.h"

const osg::StateAttribute::OverrideValue override_on = osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE;
const osg::StateAttribute::OverrideValue override_off = osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE;

class MyEventHandler : public osgGA::GUIEventHandler
{
    Cube& _cube;
    const std::string _statePath = "./cube_state.txt";
public:
    MyEventHandler(Cube& cube) :_cube(cube){}
    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&) override
    {
        if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP)
        {
            // alt
            if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_ALT)
            {
                if (ea.getUnmodifiedKey() == osgGA::GUIEventAdapter::KEY_R)
                {
                    _cube.reverse();
                }
                return false;
            }

            // ctrl
            if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_CTRL)
            {
                if (ea.getUnmodifiedKey() == osgGA::GUIEventAdapter::KEY_R)
                {
                    _cube.reset();
                }
                else if (ea.getUnmodifiedKey() == osgGA::GUIEventAdapter::KEY_S)
                {
                    std::string state = _cube.writeState();
                    std::cout << state << std::endl;
                    osgDB::ofstream out;
                    out.open(_statePath.c_str());
                    if (out.is_open())
                    {
                        out << state << std::endl;
                        out.close();
                    }
                }
                else if (ea.getUnmodifiedKey() == osgGA::GUIEventAdapter::KEY_L)
                {
                    osgDB::ifstream in;
                    in.open(_statePath.c_str());
                    if (in.is_open())
                    {
                        std::stringstream ss;
                        ss << in.rdbuf();
                        _cube.readState(ss.str());
                        in.close();
                    }
                }
                return false;
            }

            switch (ea.getKey())
            {
            case 'f': _cube.front(); break;
            case 'b': _cube.back(); break;
            case 'l': _cube.left(); break;
            case 'r': _cube.right(); break;
            case 'u': _cube.up(); break;
            case 'd': _cube.down(); break;
            case 'F': _cube.front(false); break;
            case 'B': _cube.back(false); break;
            case 'L': _cube.left(false); break;
            case 'R': _cube.right(false); break;
            case 'U': _cube.up(false); break;
            case 'D': _cube.down(false); break;
            default:
                break;
            }

        }

        return false;
    }
};

int main()
{
    if (0)
    {
        FT_Library  library;   /* handle to library     */
        FT_Init_FreeType(&library);
        FT_Done_FreeType(library);
    }

    osg::DisplaySettings::instance()->setNumMultiSamples(16);
    osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);

    osg::DisplaySettings::instance()->setVertexBufferHint(osg::DisplaySettings::VERTEX_ARRAY_OBJECT);

    osgViewer::Viewer viewer;
    viewer.setUpViewInWindow(200, 200, 800, 600);
    viewer.setCameraManipulator(new osgGA::OrbitManipulator());
    //viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);
    //viewer.addEventHandler(new osgViewer::StatsHandler);
    //viewer.addEventHandler(new osgViewer::WindowSizeHandler);
    //viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));

    osg::ref_ptr<osg::Camera> masterCamera = viewer.getCamera();
    masterCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    masterCamera->setClearColor(osg::Vec4(0.2, 0.4, 0.8, 1));

    osg::ref_ptr<osg::Group> root = new osg::Group;
    viewer.setSceneData(root);

    osg::ref_ptr<Cube> cube = new Cube;
    root->addChild(cube);
    viewer.addEventHandler(new MyEventHandler(*cube));

    viewer.home();

    osgViewer::ViewerBase::Windows windows;
    viewer.getWindows(windows);
    for (auto& window : windows)
    {
        window->getState()->setUseVertexAttributeAliasing(true);
        window->getState()->setUseModelViewAndProjectionUniforms(true);
    }

    viewer.run();
    return 0;
}
